/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * RogueUpdaterUI.java
 *
 * Created on May 15, 2010, 5:16:41 AM
 */
package rogueupdater;

import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;

/**
 *
 * @author Owner
 */
public class RogueUpdaterUI extends javax.swing.JFrame
{

    /** Creates new form RogueUpdaterUI */
    public RogueUpdaterUI()
    {
        initComponents();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        progressBar1 = new javax.swing.JProgressBar();
        comboSerialPort = new javax.swing.JComboBox();
        textFile = new javax.swing.JTextField();
        LabelFile = new javax.swing.JLabel();
        labelSerialPort = new javax.swing.JLabel();
        buttonBrowse = new javax.swing.JButton();
        buttonUpload = new javax.swing.JButton();
        buttonExit = new javax.swing.JButton();
        buttonRefresh = new javax.swing.JButton();
        labelLogo = new javax.swing.JLabel();
        labelTitle = new javax.swing.JLabel();
        labelVersion = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Rogue Updater");
        setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        setIconImage(new ImageIcon(getClass().getResource("resources/Rogue-16-32.png")).getImage());
        setResizable(false);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowOpened(java.awt.event.WindowEvent evt) {
                formWindowOpened(evt);
            }
        });

        progressBar1.setStringPainted(true);

        comboSerialPort.setToolTipText("Available serial ports.");

        textFile.setEditable(false);

        LabelFile.setText("Firmware File:");
        LabelFile.setToolTipText("Select a firmware file to upload to the module (*.rfw)");

        labelSerialPort.setText("Serial Port:");

        buttonBrowse.setText("Browse");
        buttonBrowse.setToolTipText("Select the firmware file to upload.");
        buttonBrowse.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonBrowseActionPerformed(evt);
            }
        });

        buttonUpload.setText("Upload");
        buttonUpload.setToolTipText("Upload firmware file to module.");
        buttonUpload.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonUploadActionPerformed(evt);
            }
        });

        buttonExit.setText("Exit");
        buttonExit.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonExitActionPerformed(evt);
            }
        });

        buttonRefresh.setText("Refresh");
        buttonRefresh.setToolTipText("Refresh the list of availalble serial ports.");
        buttonRefresh.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonRefreshActionPerformed(evt);
            }
        });

        labelLogo.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        labelLogo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/rogueupdater/resources/RogueLogo.png"))); // NOI18N

        labelTitle.setFont(labelTitle.getFont().deriveFont((labelTitle.getFont().getStyle() | java.awt.Font.ITALIC) | java.awt.Font.BOLD, labelTitle.getFont().getSize()+7));
        labelTitle.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        labelTitle.setText("Firmware Update Tool");
        labelTitle.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);

        labelVersion.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        labelVersion.setText("V0002");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(labelLogo, javax.swing.GroupLayout.DEFAULT_SIZE, 327, Short.MAX_VALUE)
                    .addComponent(progressBar1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 327, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(LabelFile)
                            .addComponent(labelSerialPort))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                                .addComponent(buttonUpload)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 63, Short.MAX_VALUE)
                                .addComponent(buttonExit))
                            .addComponent(comboSerialPort, javax.swing.GroupLayout.Alignment.TRAILING, 0, 179, Short.MAX_VALUE)
                            .addComponent(textFile, javax.swing.GroupLayout.DEFAULT_SIZE, 179, Short.MAX_VALUE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(buttonBrowse, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(buttonRefresh))))
                .addContainerGap())
            .addComponent(labelTitle, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 347, Short.MAX_VALUE)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addGap(150, 150, 150)
                .addComponent(labelVersion, javax.swing.GroupLayout.DEFAULT_SIZE, 47, Short.MAX_VALUE)
                .addGap(150, 150, 150))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(labelLogo)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(labelTitle)
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(textFile, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(buttonBrowse)
                    .addComponent(LabelFile))
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(comboSerialPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(buttonRefresh)
                    .addComponent(labelSerialPort))
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(buttonExit)
                    .addComponent(buttonUpload))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(progressBar1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(labelVersion))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void buttonExitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonExitActionPerformed
        System.exit(0);
    }//GEN-LAST:event_buttonExitActionPerformed

    private void buttonBrowseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonBrowseActionPerformed
        JFileChooser chooser = new JFileChooser();
        FileNameExtensionFilter filter = new FileNameExtensionFilter(
                "Rogue Firmware Files (*.rfw)", "rfw");
        // chooser.setFileFilter(filter);
        chooser.addChoosableFileFilter(filter);

        // if (!textFile.getText().isEmpty())
        // chooser.

        int returnVal = chooser.showOpenDialog(RogueUpdaterUI.this);
        if (returnVal == JFileChooser.APPROVE_OPTION)
        {
            firmwareFile = chooser.getSelectedFile();
            textFile.setText(firmwareFile.toString());
            System.out.println("You chose to open this file: " +
                    //                chooser.getSelectedFile().getName());
                    firmwareFile.toString());

        }
    }//GEN-LAST:event_buttonBrowseActionPerformed

    private void buttonUploadActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_buttonUploadActionPerformed
    {//GEN-HEADEREND:event_buttonUploadActionPerformed
        FileReader fr;
        char data[];

        try
        {
            fr = new FileReader(firmwareFile);
            data = new char[(int) firmwareFile.length()];

            int charsRead = fr.read(data);

            fr.close();

            System.out.print("bytes read: ");
            System.out.println(charsRead);
        }
        catch (Exception ex)
        {
            JOptionPane.showMessageDialog(this, "Please select a file",
                    "File Error", JOptionPane.ERROR_MESSAGE);
            return;
        }

        byte fwData[] = new byte[data.length / 2];

        // convert to binary
        for (int i = 0; i < data.length; i += 2)
        {
            fwData[i / 2] = (byte) ((Character.digit(data[i], 16) << 4) +
                    Character.digit(data[i + 1], 16));
        }

        // got the data, let's upload
        // need to make sure serial port is ready
        // open the port now, send data, then close the port when done

        // open the port
        openSerialPort();

/*
        try { Thread.sleep(2000L); }
        catch (Exception e) { }

        try {
            while (input.available() > 0)
            {
                byte x = (byte) input.read();
                System.out.printf("Dumped: %02x\n", x);
            }
        }
        catch (Exception e) { }
*/
        // send the data
        int index = 0;
        int frameSize = 0;
        byte receivedByte;
        byte retries = 0;

        progressBar1.setMinimum(0);
        progressBar1.setValue(0);
        progressBar1.setMaximum(fwData.length);

        for (index = 0; index < fwData.length; index += frameSize)
        {
            frameSize = (fwData[index] & 0xff) << 8;
            frameSize += (fwData[index + 1] & 0xff) + 2;

            try
            {
                output.flush();

                //System.out.println("Transferring: " + index + "/" + fwData.length + " (" + int(100*index/fwData.length) + "%)");

                progressBar1.setValue(index);
                progressBar1.paintImmediately(progressBar1.getVisibleRect());

                byte[] bytesToSend = new byte[frameSize];

                for (int i = 0; i < frameSize; i++)
                {
                    bytesToSend[i] = fwData[index + i];
                }

                output.write(bytesToSend);

                int timeout = 0;
                // now get response
                while (input.available() <= 0)
                {
                    
                    Thread.sleep(20);
                    if (timeout++ >= 150)
                    {
                        // println("Timeout waiting for response.  Update failed.");
                        errorMessage(
                                "Timeout waiting for response. Update failed.",
                                "Update Error",
                                null);
                        closeSerialPort();
                        return;
                    }
                }

                receivedByte = (byte) input.read();

                switch (receivedByte)
                {
                    case 0x11:
                        retries = 0;
                        break;
                    case 0x22:
                    default:
                        retries++;
                        if (retries > 4)
                        {
                            errorMessage("Cannot communicate with module. Is it in Update mode?", "Update Error", null);
                            closeSerialPort();
                            return;
                        }
                        else
                        {
                            index -= frameSize;
                            // println("Retry");
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                errorMessage("Upload Error. Maybe something got disconnected?", "Error", ex);
                break;
            }
        }


        closeSerialPort();
    }//GEN-LAST:event_buttonUploadActionPerformed

    private void buttonRefreshActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_buttonRefreshActionPerformed
    {//GEN-HEADEREND:event_buttonRefreshActionPerformed
        populateComboSerial();
    }//GEN-LAST:event_buttonRefreshActionPerformed

    private void formWindowOpened(java.awt.event.WindowEvent evt)//GEN-FIRST:event_formWindowOpened
    {//GEN-HEADEREND:event_formWindowOpened
        populateComboSerial();
    }//GEN-LAST:event_formWindowOpened

    private void populateComboSerial()
    {
        String serialports[] = serialList();

        comboSerialPort.removeAllItems();

        if (serialports.length > 0)
        {
            for (int i = 0; i < serialports.length; i++)
            {
                comboSerialPort.addItem(serialports[i]);
            }
        }
    }

    public String[] serialList()
    {
        Vector list = new Vector();
        try
        {
            //System.err.println("trying");
            Enumeration portList = CommPortIdentifier.getPortIdentifiers();
            //System.err.println("got port list");
            while (portList.hasMoreElements())
            {
                CommPortIdentifier pId =
                        (CommPortIdentifier) portList.nextElement();
                //System.out.println(portId);

                if (pId.getPortType() == CommPortIdentifier.PORT_SERIAL)
                {
                    String name = pId.getName();
                    list.addElement(name);
                }
            }

        }
        catch (UnsatisfiedLinkError e)
        {
            //System.err.println("1");
//            errorMessage("ports", "", e);
            e.printStackTrace();

        }
        catch (Exception e)
        {
            //System.err.println("2");
//            errorMessage("ports", e);
            e.printStackTrace();
        }
        //System.err.println("move out");
        String outgoing[] = new String[list.size()];
        list.copyInto(outgoing);
        return outgoing;
    }

    public void errorMessage(String msg, String title, Throwable e)
    {
        System.err.println(msg);
        JOptionPane.showMessageDialog(this, msg, title,
                JOptionPane.ERROR_MESSAGE);
        if (e != null)
        {
            e.printStackTrace();
        }
    }

    public boolean openSerialPort()
    {
        boolean portFound = false;

        if (comboSerialPort.getSelectedItem() != null)
        {
            try
            {
                portId = CommPortIdentifier.getPortIdentifier(
                        (String) comboSerialPort.getSelectedItem());
            }
            catch (Exception ex)
            {
                errorMessage("Can't get Identifier", "Serial Port Open Error", ex);
                return false;
            }

            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL)
            {
                System.out.println("Found port: " +
                        comboSerialPort.getSelectedItem());
                portFound = true;

                try
                {
                    serialPort = (SerialPort) portId.open("RUSerial", 2000);
                    input = serialPort.getInputStream();
                    output = serialPort.getOutputStream();
                    serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,
                            SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
                }
                catch (Exception ex)
                {
                    errorMessage("Serial Port Error", "Serial Port Open Error", ex);
                }
            }
            else
            {
                errorMessage("Wrong port type!?", "Serial Port Open Error", null);
            }

            if (!portFound)
            {
                System.out.println("port " + comboSerialPort.getSelectedItem() +
                        " not found.");
            }
        }

        return portFound;
    }

    public void closeSerialPort()
    {
        if (serialPort != null)
        {
            serialPort.close();
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[])
    {
        java.awt.EventQueue.invokeLater(new Runnable()
        {

            public void run()
            {
                new RogueUpdaterUI().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel LabelFile;
    private javax.swing.JButton buttonBrowse;
    private javax.swing.JButton buttonExit;
    private javax.swing.JButton buttonRefresh;
    private javax.swing.JButton buttonUpload;
    private javax.swing.JComboBox comboSerialPort;
    private javax.swing.JLabel labelLogo;
    private javax.swing.JLabel labelSerialPort;
    private javax.swing.JLabel labelTitle;
    private javax.swing.JLabel labelVersion;
    private javax.swing.JProgressBar progressBar1;
    private javax.swing.JTextField textFile;
    // End of variables declaration//GEN-END:variables
    private File firmwareFile;
    private SerialPort serialPort;
    private InputStream input;
    private OutputStream output;
    private CommPortIdentifier portId;
}
